package com.gitee.cashzhang27.test.sdk.bestsign.core;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.gitee.cashzhang27.test.sdk.bestsign.core.util.CryptUtils;
import com.gitee.cashzhang27.test.sdk.bestsign.domain.AbstractBestSignResponse;
import com.gitee.cashzhang27.test.sdk.bestsign.domain.BestSignRequest;
import com.gitee.cashzhang27.test.sdk.bestsign.domain.DefaultBestSignResponse;
import com.google.common.base.Stopwatch;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;

/**
 * @author Cash Zhang
 * @version v1.0
 * @since 2019/08/05 15:23
 */
@Slf4j
public class AbstractBestSignClient implements BestSignClient {

  private final static String URL_SIGN_PARAMS = "?developerId=%s&rtick=%s&signType=rsa&sign=%s";

  @Override
  public <T extends AbstractBestSignResponse> T execute(BestSignRequest<T> request)
      throws Exception {
    String resultJson = null;
    Stopwatch stopwatch = Stopwatch.createStarted();
    String privateKey = request.getPrivateKey();
    String developerId = request.getDeveloperId();
    String serverHost = request.getServerHost();
    String rtick = CryptUtils.getRtick();
    Method methodType = request.getMethodType();
    String methodName = request.getMethodName();

    request.clearImportantParams();

    if (Method.GET.equals(methodType)) {
      String url = beforeGetMethod(request, privateKey, developerId, serverHost, rtick, methodType,
          methodName);
      resultJson = HttpUtil.createGet(url).execute().body();
    } else if (Method.POST.equals(methodType)) {
      JSONObject jsonObject = JSONUtil.parseObj(request);
      jsonObject.put("methodName", null);
      jsonObject.put("methodType", null);
      String bodyOriginalJson = JSONUtil.toJsonStr(jsonObject);
      // 获取签名
      String sign = CryptUtils
          .sign(developerId, privateKey, serverHost, methodName, methodType, rtick, null,
              bodyOriginalJson);

      log.info("request best sign sign is : {}", sign);
      // 签名参数追加为url参数
      String urlParams = String.format(URL_SIGN_PARAMS, developerId, rtick, sign);
      String url = serverHost + methodName + urlParams;
      log.info("request best sign url is : {}", url);

      bodyOriginalJson = JSONUtil.toJsonStr(jsonObject);
      log.info("request best sign json is : {}", bodyOriginalJson);
      HttpRequest httpRequest = HttpUtil.createPost(url);
      resultJson = httpRequest.body(bodyOriginalJson).execute().body();
    }

    log.info("best sign return json is : {}", resultJson);
    if (!JSONUtil.isJson(resultJson)) {
      throw new RuntimeException("occur a exception because best sign return not json.");
    }
    DefaultBestSignResponse defaultBestSignResponse = JSONUtil
        .toBean(resultJson, DefaultBestSignResponse.class);

    if (!defaultBestSignResponse.isSuccess()) {
      throw new RuntimeException(
          "errorno：" + defaultBestSignResponse.getErrno() + ",errmsg：" + defaultBestSignResponse
              .getErrmsg());
    }

    String data = defaultBestSignResponse.getData();
    Class<T> responseClass = request.getResponseClass();
    log.info(
        "the method executed successfully, taking [{}] , with [{}] on the best sign interface.",
        stopwatch.stop().toString(), defaultBestSignResponse.getCost());

    return JSONUtil.toBean(data, responseClass);
  }

  <T extends AbstractBestSignResponse> String beforeGetMethod(BestSignRequest<T> request,
      String privateKey, String developerId, String serverHost, String rtick, Method methodType,
      String methodName) throws Exception {
    Map<String, String> paramMap = Convert.convert(new TypeReference<Map<String, String>>() {
    }, request);
    paramMap.remove("methodName");
    paramMap.remove("methodType");
    paramMap.remove("developerId");
    paramMap.remove("privateKey");
    paramMap.remove("serverHost");
    // 获取签名
    String sign = CryptUtils
        .sign(developerId, privateKey, serverHost, methodName, methodType, rtick, paramMap,
            null);
    paramMap.put("developerId", developerId);
    paramMap.put("rtick", rtick);
    paramMap.put("signType", "rsa");
    paramMap.put("sign", sign);
    String url = serverHost + methodName + "?" + HttpUtil.toParams(paramMap);
    log.info("request best sign url is : {}", url);
    return url;
  }


}
